package com.lc.learn.fileana.utils;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.SimpleBookmark;
import com.itextpdf.text.pdf.parser.PdfTextExtractor;
import com.lc.learn.fileana.common.constant.AnalyseConstant;
import com.lc.learn.fileana.common.enums.FileTypeEnum;
import com.lc.learn.fileana.entity.dto.FileAnalyseResDto;
import com.lc.learn.fileana.entity.dto.StyleTextDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.usermodel.Paragraph;
import org.apache.poi.hwpf.usermodel.Range;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFStyle;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.charset.Charset;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @description: some desc
 * @author: LChuang
 * @date: 2023/10/30 15:24
 */
@Slf4j
public class FileAnalyseUtil {

    /**
     * 文件解析(只解析doc,docx,pdf,txt,md类型文件)
     * @param file 上传的文件
     */
    public static List<FileAnalyseResDto> analyseFile(MultipartFile file) {
        List<StyleTextDto> textVos;
        List<FileAnalyseResDto> dtoList = null;
        String suffix = Objects.requireNonNull(file.getOriginalFilename()).substring(file.getOriginalFilename().lastIndexOf("."));
        // 解析文档并生成文件子信息列表
        if (suffix.equals(FileTypeEnum.DOC.getType())) {
            textVos = analyseDoc(file);
            dtoList = createInfoChildList(textVos, file, suffix);
        }
        if (suffix.equals(FileTypeEnum.DOCX.getType())) {
            textVos = analyseDocx(file);
            dtoList = createInfoChildList(textVos, file, suffix);
        }
        if (suffix.equals(FileTypeEnum.PDF.getType())) {
            textVos = analysePdf(file);
            dtoList = createInfoChildList(textVos, file, suffix);
        }
        if (suffix.equals(FileTypeEnum.TXT.getType())) {
            textVos = analyseTxt(file);
            dtoList = createInfoChildList(textVos, file, suffix);
        }
        if (suffix.equals(FileTypeEnum.MD.getType())) {
            textVos = analyseMd(file);
            dtoList = createInfoChildList(textVos, file, suffix);
        }
        // 不解析的文件类型
        if(AnalyseConstant.NOT_ANALYSE_SUFFIX.contains(suffix)){
            dtoList = Lists.newArrayList();
            FileAnalyseResDto resDto = new FileAnalyseResDto();
            resDto.setTitle(file.getName());
            dtoList.add(resDto);
        }
        return dtoList;
    }

    /**
     * 解析.doc文件
     */
    private static List<StyleTextDto> analyseDoc(MultipartFile file) {
        List<StyleTextDto> styleTextDtoList = new ArrayList<>();
        try {
            FileInputStream inputStream = new FileInputStream(Objects.requireNonNull(multipartFileToFile(file)));
            HWPFDocument doc = new HWPFDocument(inputStream);
            Range r = doc.getRange();
            for (int i = 0; i < r.numParagraphs(); i++) {
                Paragraph p = r.getParagraph(i);
                int styleIndex = p.getStyleIndex();
                String styleName = doc.getStyleSheet().getStyleDescription(styleIndex).getName();
                int titleLevel = AnalyseConstant.DEFAULT_LEVEL; // 默认为正文
                if (styleName.equals(AnalyseConstant.DOC_TEXT_PREFIX)) {
                    styleName = AnalyseConstant.REPLACE_TEXT_PREFIX;
                } else {
                    titleLevel = styleIndex;
                    // 修改标题显示
                    styleName = styleName.replace(AnalyseConstant.DOC_TITLE_PREFIX, AnalyseConstant.REPLACE_TITLE_PREFIX);
                }
                StyleTextDto styleTextDto = StyleTextDto.builder().titleLevel(titleLevel).style(styleName).text(p.text()).build();
                styleTextDtoList.add(styleTextDto);
            }
            doc.close();
            return styleTextDtoList;
        } catch (IOException e) {
            log.error("解析doc文件异常:{}", ExceptionUtil.stacktraceToString(e));
            return styleTextDtoList;
        }
    }

    /**
     * 解析.docx文件
     */
    private static List<StyleTextDto> analyseDocx(MultipartFile file) {
        List<StyleTextDto> styleTextDtoList = new ArrayList<>();
        try {
            FileInputStream inputStream = new FileInputStream(Objects.requireNonNull(multipartFileToFile(file)));
            XWPFDocument document = new XWPFDocument(inputStream);
            List<XWPFParagraph> paragraphList = document.getParagraphs();
            for (XWPFParagraph paragraph : paragraphList) {
                String styleId = paragraph.getStyleID();
                String styleName = AnalyseConstant.DOC_TEXT_PREFIX;// 默认风格 文本
                int titleLevel = AnalyseConstant.DEFAULT_LEVEL; // 默认标题等级 -1
                if (!StrUtil.isEmpty(styleId)) {
                    try {
                        titleLevel = Integer.parseInt(styleId);
                        XWPFStyle style = document.getStyles().getStyle(styleId);
                        styleName = style.getName();
                    } catch (Exception e) {
                        log.error("不支持的标题格式, msg:{}", e.getMessage());
                    }
                }
                if (styleId == null) {
                    styleName = AnalyseConstant.REPLACE_TEXT_PREFIX;
                } else {
                    // 修改标题显示
                    styleName = styleName.replace(AnalyseConstant.DOCX_TITLE_PREFIX, AnalyseConstant.REPLACE_TITLE_PREFIX);
                }
                StyleTextDto styleTextDto = StyleTextDto.builder().titleLevel(titleLevel).style(styleName).text(paragraph.getText()).build();
                styleTextDtoList.add(styleTextDto);
            }
            return styleTextDtoList;
        } catch (IOException e) {
            log.error("解析docx文件异常:{}", ExceptionUtil.stacktraceToString(e));
            return styleTextDtoList;
        }
    }

    /**
     * 解析.pdf文件
     */
    private static List<StyleTextDto> analysePdf(MultipartFile file) {
        List<StyleTextDto> styleTextDtoList = new ArrayList<>();
        try {
            FileInputStream inputStream = new FileInputStream(Objects.requireNonNull(multipartFileToFile(file)));
            PdfReader pdfReader = new PdfReader(inputStream);
            // 标题map队列
            List<HashMap<String, Object>> bookmark = SimpleBookmark.getBookmark(pdfReader);
            for (int i = 0; i < bookmark.size(); i++) {
                // 标题list为空时，只保存正文
                if (CollUtil.isNotEmpty(bookmark)) {
                    getPdfTitle(bookmark.get(i), styleTextDtoList, i);
                }
                // 读取每一页正文内容的一半(折中方案)
                String text = PdfTextExtractor.getTextFromPage(pdfReader, i + 1);
                styleTextDtoList.add(StyleTextDto.builder().titleLevel(AnalyseConstant.DEFAULT_LEVEL).style(AnalyseConstant.REPLACE_TEXT_PREFIX).text(text.substring(0, text.length() / 2)).build());
            }
            return styleTextDtoList;
        } catch (Exception e) {
            log.error("解析pdf文件异常:{}", ExceptionUtil.stacktraceToString(e));
            return styleTextDtoList;
        }
    }

    /**
     * 获取pdf标题
     */
    private static void getPdfTitle(HashMap<String, Object> bookmark, List<StyleTextDto> styleTextDtoList, Integer num) {
        String h1Text = (String) bookmark.get(AnalyseConstant.PDF_TITLE_DEFAULT);
        styleTextDtoList.add(StyleTextDto.builder().titleLevel(1).style("h1").text(h1Text).pdfPage(num + 1).build());
        List<HashMap<String, Object>> kids = (ArrayList<HashMap<String, Object>>) bookmark.get(AnalyseConstant.PDF_KIDS_DEFAULT);
        if(!CollUtil.isEmpty(kids)){
            for (HashMap<String, Object> kid : kids) {
                getPdfTitle(kid, styleTextDtoList, num);
            }
        }
    }

    /**
     * 解析.txt文件
     */
    private static List<StyleTextDto> analyseTxt(MultipartFile file) {
        List<StyleTextDto> styleTextDtoList = new ArrayList<>();
        StringBuilder content = new StringBuilder();
        try {
            BufferedReader reader = new BufferedReader(
                        new InputStreamReader(new FileInputStream(Objects.requireNonNull(multipartFileToFile(file))), Charset.forName("GBK")));
            String s;
            while ((s = reader.readLine()) != null) {
                content.append(System.lineSeparator()).append(s.replaceAll(AnalyseConstant.REGEXP, AnalyseConstant.REPLACE));
            }
            reader.close();
            styleTextDtoList.add(StyleTextDto.builder()
                    .titleLevel(AnalyseConstant.DEFAULT_LEVEL)
                    .style(AnalyseConstant.REPLACE_TEXT_PREFIX)
                    .text(content.toString()).build());
            return styleTextDtoList;
        } catch (IOException e) {
            log.error("解析txt文件异常:{}", ExceptionUtil.stacktraceToString(e));
            return styleTextDtoList;
        }
    }

    /**
     * 解析.md文件
     */
    private static List<StyleTextDto> analyseMd(MultipartFile file) {
        List<StyleTextDto> styleTextDtoList = new ArrayList<>();
        try {
            BufferedReader reader = new BufferedReader(new FileReader(Objects.requireNonNull(multipartFileToFile(file))));
            String s;
            while ((s = reader.readLine()) != null) {
                if (s.contains(AnalyseConstant.MD_TITLE_PREFIX)) {
                    s = s.replaceAll(AnalyseConstant.MD_TITLE_PREFIX, "");
                    styleTextDtoList.add(StyleTextDto.builder().titleLevel(AnalyseConstant.MD_TITLE_LEVEL).style(AnalyseConstant.MD_TITLE_DEFAULT).text(s).build());
                    continue;
                }
                // md文件在源码编译模式下，每一段后面会有一个换行，所以此处做了判断
                if(!s.equals(AnalyseConstant.REPLACE)){
                    styleTextDtoList.add(StyleTextDto.builder().titleLevel(AnalyseConstant.DEFAULT_LEVEL).style(AnalyseConstant.REPLACE_TEXT_PREFIX).text(s).build());
                }
            }
            return styleTextDtoList;
        } catch (IOException e) {
            log.error("解析markdown文件异常:{}", ExceptionUtil.stacktraceToString(e));
            return styleTextDtoList;
        }
    }

    /**
     * 创建文件信息子对象列表
     */
    private static List<FileAnalyseResDto> createInfoChildList(List<StyleTextDto> dtoList, MultipartFile file, String suffix){
        List<FileAnalyseResDto> resDtoList = Lists.newArrayList();
        // 解析后的文档不存在标题。求交集，判断是否存在标题(.txt文件走以下逻辑)
        List<String> styleList = dtoList.stream().map(StyleTextDto::getStyle).collect(Collectors.toList());
        if (CollUtil.isEmpty(CollUtil.intersection(styleList, AnalyseConstant.WORD_TITLE_LIST))) {
            String content = dtoList.stream().map(StyleTextDto::getText).collect(Collectors.joining(""));
            FileAnalyseResDto resDto = new FileAnalyseResDto();
            resDto.setTitle(file.getName());
            // 数据库最大长度5000，超过截取到3000
            resDto.setContent(content.replaceAll(AnalyseConstant.REGEXP, AnalyseConstant.REPLACE).length() > 5000 ? content.substring(0, 3000) : content);
            resDtoList.add(resDto);
            return resDtoList;
        }

        // 解析后的文档存在标题
        if (suffix.equals(FileTypeEnum.DOC.getType()) || suffix.equals(FileTypeEnum.DOCX.getType()) || suffix.equals(FileTypeEnum.MD.getType())) {
            for (int i = 0; i < dtoList.size(); i++) {
                if (dtoList.get(i).getStyle().contains(AnalyseConstant.REPLACE_TITLE_PREFIX)) {
                    // 拼接每个标题后面的正文内容
                    StringBuilder content = new StringBuilder();
                    for (int j = i + 1; j < dtoList.size(); j++) {
                        if (dtoList.get(j).getStyle().contains(AnalyseConstant.REPLACE_TITLE_PREFIX)) {
                            break;
                        }
                        content.append(dtoList.get(j).getText().replaceAll(AnalyseConstant.REGEXP, AnalyseConstant.REPLACE));
                    }
                    FileAnalyseResDto resDto = new FileAnalyseResDto();
                    resDto.setTitle(dtoList.get(i).getText());
                    resDto.setContent(content.toString().replaceAll(AnalyseConstant.REGEXP, AnalyseConstant.REPLACE).length() > 5000 ? content.substring(0, 3000) : content.toString());
                    resDtoList.add(resDto);
                }
            }
        }
        if (suffix.equals(FileTypeEnum.PDF.getType())) {
            // pdf正文列表
            List<StyleTextDto> contentDtoList = dtoList.stream().filter(e -> e.getStyle().contains(AnalyseConstant.REPLACE_TEXT_PREFIX)).collect(Collectors.toList());
            // pdf标题列表
            List<StyleTextDto> titleDtoList = dtoList.stream().filter(e -> e.getStyle().contains(AnalyseConstant.REPLACE_TITLE_PREFIX)).collect(Collectors.toList());
            for (StyleTextDto contentDto : contentDtoList) {
                for (StyleTextDto titleDto : titleDtoList) {
                    if (contentDto.getPdfPage().equals(titleDto.getPdfPage())) {
                        FileAnalyseResDto resDto = new FileAnalyseResDto();
                        resDto.setTitle(titleDto.getText());
                        resDto.setContent(contentDto.getText());
                        resDtoList.add(resDto);
                    }
                }
            }
        }
        return resDtoList;
    }

    /**
     * 文件转换，MultipartFile-->File
     */
    private static File multipartFileToFile(MultipartFile multiFile) {
        String fileName = multiFile.getOriginalFilename();
        assert fileName != null;
        String prefix = fileName.substring(fileName.lastIndexOf("."));
        try {
            File file = File.createTempFile("annex", prefix);
            multiFile.transferTo(file);
            return file;
        } catch (Exception e) {
            log.error("MultipartFile文件转换异常：{}", ExceptionUtil.stacktraceToString(e));
        }
        return null;
    }

}
